package org.breathe.rdb;

import lombok.extern.slf4j.Slf4j;
import org.breathe.database.RedisDB;
import org.breathe.datastruct.RedisBytes;
import org.breathe.datastruct.RedisData;
import org.breathe.datastruct.impl.*;
import org.breathe.server.core.RedisCore;

import java.io.*;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;

/**
 * @author: breathe
 * @createTime: 2025-06-08
 */
@Slf4j
public class RdbWriter {
    private RedisCore redisCore;
    private final AtomicBoolean running = new AtomicBoolean(false);
    public RdbWriter(RedisCore redisCore) {
        this.redisCore = redisCore;
    }
    public boolean writeRdb(String fileName) {
        if (running.get()) {
            return false;
        }
        running.set(true);
        File tempFile = new File(fileName);
        try (DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(tempFile)))){
            RdbUtils.writeRdbHeader(dos);
            saveAllDatabases(dos);
            RdbUtils.writeRdbTail(dos);
        } catch (Exception e) {
            log.error("rdb write fail", e);
            return false;
        }
        log.info("rdb write completely");
        return true;
    }

    private void saveAllDatabases(DataOutputStream dos) throws IOException {
        RedisDB[] redisDatabases = redisCore.getDatabases();
        for (RedisDB db : redisDatabases) {
            log.info("saving database {}", db.getId());
            if (db.getId() > 0) {
                writeDb(dos, db);
            }
        }
    }

    private void writeDb(DataOutputStream dos, RedisDB db) throws IOException {
        int databaseId = db.getId();
        RdbUtils.writeSelectDB(dos, databaseId);
        for (Map.Entry<RedisBytes, Object> entry : db.getData().entrySet()) {
            RedisBytes key = entry.getKey();
            RedisData value = (RedisData) entry.getValue();
            rdbSaveObject(dos, key, value);
        }
    }

    private void rdbSaveObject(DataOutputStream dos, RedisBytes key, RedisData value) throws IOException {
        switch(value.getClass().getSimpleName()) {
            //TODO impl all
            case "RedisString":
                RdbUtils.saveString(dos, key, (RedisString) value);
                log.info("save string: {}", key);
                break;
            case "RedisList":
                RdbUtils.saveList(dos, key, (RedisList) value);
                log.info("save list: {}", key);
                break;
            case "RedisSet":
                RdbUtils.saveSet(dos, key, (RedisSet) value);
                log.info("save set: {}", key);
                break;
            case "RedisHash":
                RdbUtils.saveHash(dos, key, (RedisHash) value);
                log.info("save hash: {}", key);
                break;
            case "RedisZSet":
                RdbUtils.saveZSet(dos, key, (RedisZSet) value);
                log.info("save zset: {}", key);
                break;
        }
    }
    public void close() {
        if (running.get()) {
            running.set(false);
        }
    }
}
